﻿//Exchange.cs
//Copyright (c) 2013 StockSharp LLC, all rights reserved.
//This code module is part of StockSharp library.
//This code is licensed under the GNU GENERAL PUBLIC LICENSE Version 3.
//See the file License.txt for the license details.
//More info on: http://stocksharp.com

namespace StockSharp.BusinessEntities
{
	using System;
	using System.Collections.Generic;
	using System.ComponentModel;
	using System.Linq;
	using System.Runtime.Serialization;
	using System.Xml.Serialization;

	using Ecng.Common;
	using Ecng.ComponentModel;
	using Ecng.Serialization;

	/// <summary>
	/// Информация о бирже.
	/// </summary>
	[Serializable]
	[System.Runtime.Serialization.DataContract]
	[Ignore(FieldName = "IsDisposed")]
	public class Exchange : Equatable<Exchange>, IExtendableEntity
	{
		static Exchange()
		{
			Test = new Exchange
			{
				Name = "TEST",
				RusName = "Тестовая биржа",
				EngName = "Test Exchange",
				TimeZoneInfo = TimeZoneInfo.Utc,
			};

			Me = new Exchange
			{
				Name = "ME",
				RusName = "Московская биржа",
				EngName = "Moscow Exchange",
				TimeZoneInfo = TimeZoneInfo.FromSerializedString("Russian Standard Time;240;(GMT+04:00) Moscow, St. Petersburg, Volgograd;Russian Standard Time;Russian Daylight Time;[01:01:0001;12:31:2010;60;[0;02:00:00;3;5;0;];[0;03:00:00;10;5;0;];];"),
			};

			Ux = new Exchange
			{
				Name = "UX",
				RusName = "Украинская биржа",
				EngName = "Ukrain Exchange",
				TimeZoneInfo = TimeZoneInfo.FromSerializedString("FLE Standard Time;120;(GMT+02:00) Helsinki, Kyiv, Riga, Sofia, Tallinn, Vilnius;FLE Standard Time;FLE Daylight Time;[01:01:0001;12:31:9999;60;[0;03:00:00;3;5;0;];[0;04:00:00;10;5;0;];];"),
			};

			var americanTime = TimeZoneInfo.FromSerializedString("Eastern Standard Time;-300;(UTC-05:00) Eastern Time (US & Canada);Eastern Standard Time;Eastern Daylight Time;[01:01:0001;12:31:2006;60;[0;02:00:00;4;1;0;];[0;02:00:00;10;5;0;];][01:01:2007;12:31:9999;60;[0;02:00:00;3;2;0;];[0;02:00:00;11;1;0;];];");

			Nyse = new Exchange
			{
				Name = "NYSE",
				TimeZoneInfo = americanTime,
			};

			Nasdaq = new Exchange
			{
				Name = "NASDAQ",
				TimeZoneInfo = americanTime,
			};

			Tsx = new Exchange
			{
				Name = "TSX",
				TimeZoneInfo = americanTime,
			};

			Lse = new Exchange
			{
				Name = "LSE",
				TimeZoneInfo = TimeZoneInfo.FromSerializedString("GMT Standard Time;0;(UTC) Dublin, Edinburgh, Lisbon, London;GMT Standard Time;GMT Daylight Time;[01:01:0001;12:31:9999;60;[0;01:00:00;3;5;0;];[0;02:00:00;10;5;0;];];"),
			};

			Tse = new Exchange
			{
				Name = "TSE",
				TimeZoneInfo = TimeZoneInfo.FromSerializedString("Tokyo Standard Time;540;(UTC+09:00) Osaka, Sapporo, Tokyo;Tokyo Standard Time;Tokyo Daylight Time;;"),
			};

			var chinaTime = TimeZoneInfo.FromSerializedString("China Standard Time;480;(UTC+08:00) Beijing, Chongqing, Hong Kong, Urumqi;China Standard Time;China Daylight Time;;");

			Hkex = new Exchange
			{
				Name = "HKEX",
				TimeZoneInfo = chinaTime,
			};

			Hkfe = new Exchange
			{
				Name = "HKFE",
				TimeZoneInfo = chinaTime,
			};

			Sse = new Exchange
			{
				Name = "SSE",
				TimeZoneInfo = chinaTime,
			};

			Szse = new Exchange
			{
				Name = "SZSE",
				TimeZoneInfo = chinaTime,
			};

			Tsec = new Exchange
			{
				Name = "TSEC",
				TimeZoneInfo = chinaTime,
			};

			var singaporeTime = TimeZoneInfo.FromSerializedString("Singapore Standard Time;480;(UTC+08:00) Kuala Lumpur, Singapore;Malay Peninsula Standard Time;Malay Peninsula Daylight Time;;");

			Sgx = new Exchange
			{
				Name = "SGX",
				TimeZoneInfo = singaporeTime,
			};

			Pse = new Exchange
			{
				Name = "PSE",
				TimeZoneInfo = singaporeTime,
			};

			Myx = new Exchange
			{
				Name = "MYX",
				TimeZoneInfo = singaporeTime,
			};

			var bangkokTime = TimeZoneInfo.FromSerializedString("SE Asia Standard Time;420;(UTC+07:00) Bangkok, Hanoi, Jakarta;SE Asia Standard Time;SE Asia Daylight Time;;");

			Idx = new Exchange
			{
				Name = "IDX",
				TimeZoneInfo = bangkokTime,
			};

			Set = new Exchange
			{
				Name = "SET",
				TimeZoneInfo = bangkokTime,
			};

			var indiaTime = TimeZoneInfo.FromSerializedString("India Standard Time;330;(UTC+05:30) Chennai, Kolkata, Mumbai, New Delhi;India Standard Time;India Daylight Time;;");

			Bse = new Exchange
			{
				Name = "BSE",
				TimeZoneInfo = indiaTime,
			};

			Nse = new Exchange
			{
				Name = "NSE",
				TimeZoneInfo = indiaTime,
			};

			Cse = new Exchange
			{
				Name = "CSE",
				TimeZoneInfo = TimeZoneInfo.FromSerializedString("Sri Lanka Standard Time;330;(UTC+05:30) Sri Jayawardenepura;Sri Lanka Standard Time;Sri Lanka Daylight Time;;"),
			};

			Krx = new Exchange
			{
				Name = "KRX",
				TimeZoneInfo = TimeZoneInfo.FromSerializedString("Korea Standard Time;540;(UTC+09:00) Seoul;Korea Standard Time;Korea Daylight Time;;"),
			};

			Asx = new Exchange
			{
				Name = "ASX",
				TimeZoneInfo = TimeZoneInfo.FromSerializedString("AUS Eastern Standard Time;600;(UTC+10:00) Canberra, Melbourne, Sydney;AUS Eastern Standard Time;AUS Eastern Daylight Time;[01:01:0001;12:31:2007;60;[0;02:00:00;10;5;0;];[0;03:00:00;3;5;0;];][01:01:2008;12:31:9999;60;[0;02:00:00;10;1;0;];[0;03:00:00;4;1;0;];];"),
			};

			Nzsx = new Exchange
			{
				Name = "NZSX",
				TimeZoneInfo = TimeZoneInfo.FromSerializedString("New Zealand Standard Time;720;(UTC+12:00) Auckland, Wellington;New Zealand Standard Time;New Zealand Daylight Time;[01:01:0001;12:31:2006;60;[0;02:00:00;10;1;0;];[0;03:00:00;3;3;0;];][01:01:2007;12:31:2007;60;[0;02:00:00;9;5;0;];[0;03:00:00;3;3;0;];][01:01:2008;12:31:9999;60;[0;02:00:00;9;5;0;];[0;03:00:00;4;1;0;];];"),
			};

			Tase = new Exchange
			{
				Name = "TASE",
				TimeZoneInfo = TimeZoneInfo.FromSerializedString("Israel Standard Time;120;(UTC+02:00) Jerusalem;Jerusalem Standard Time;Jerusalem Daylight Time;[01:01:2005;12:31:2005;60;[0;02:00:00;4;1;5;];[0;02:00:00;10;2;0;];][01:01:2006;12:31:2006;60;[0;02:00:00;3;5;5;];[0;02:00:00;10;1;0;];][01:01:2007;12:31:2007;60;[0;02:00:00;3;5;5;];[0;02:00:00;9;3;0;];][01:01:2008;12:31:2008;60;[0;02:00:00;3;5;5;];[0;02:00:00;10;1;0;];][01:01:2009;12:31:2009;60;[0;02:00:00;3;5;5;];[0;02:00:00;9;5;0;];][01:01:2010;12:31:2010;60;[0;02:00:00;3;5;5;];[0;02:00:00;9;2;0;];][01:01:2011;12:31:2011;60;[0;02:00:00;4;1;5;];[0;02:00:00;10;1;0;];][01:01:2012;12:31:2012;60;[0;02:00:00;3;5;5;];[0;02:00:00;9;4;0;];][01:01:2013;12:31:2013;60;[0;02:00:00;3;5;5;];[0;02:00:00;9;2;0;];][01:01:2014;12:31:2014;60;[0;02:00:00;3;5;5;];[0;02:00:00;9;4;0;];][01:01:2015;12:31:2015;60;[0;02:00:00;3;5;5;];[0;02:00:00;9;3;0;];][01:01:2016;12:31:2016;60;[0;02:00:00;4;1;5;];[0;02:00:00;10;2;0;];][01:01:2017;12:31:2017;60;[0;02:00:00;3;5;5;];[0;02:00:00;9;4;0;];][01:01:2018;12:31:2018;60;[0;02:00:00;3;5;5;];[0;02:00:00;9;3;0;];][01:01:2019;12:31:2019;60;[0;02:00:00;3;5;5;];[0;02:00:00;10;1;0;];][01:01:2020;12:31:2020;60;[0;02:00:00;3;5;5;];[0;02:00:00;9;4;0;];][01:01:2021;12:31:2021;60;[0;02:00:00;3;5;5;];[0;02:00:00;9;2;0;];][01:01:2022;12:31:2022;60;[0;02:00:00;4;1;5;];[0;02:00:00;10;1;0;];];"),
			};

			Fwb = new Exchange
			{
				Name = "FWB",
				TimeZoneInfo = TimeZoneInfo.FromSerializedString("W. Europe Standard Time;60;(UTC+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna;W. Europe Standard Time;W. Europe Daylight Time;[01:01:0001;12:31:9999;60;[0;02:00:00;3;5;0;];[0;03:00:00;10;5;0;];];"),
			};

			Mse = new Exchange
			{
				Name = "MSE",
				TimeZoneInfo = TimeZoneInfo.FromSerializedString("Romance Standard Time;60;(UTC+01:00) Brussels, Copenhagen, Madrid, Paris;Romance Standard Time;Romance Daylight Time;[01:01:0001;12:31:9999;60;[0;02:00:00;3;5;0;];[0;03:00:00;10;5;0;];];"),
			};

			Swx = new Exchange
			{
				Name = "SWX",
				TimeZoneInfo = GetTimeZone("Central European Standard Time", TimeSpan.FromHours(1))
			};

			Jse = new Exchange
			{
				Name = "JSE",
				TimeZoneInfo = GetTimeZone("South Africa Standard Time", TimeSpan.FromHours(2))
			};
		}

		private static TimeZoneInfo GetTimeZone(string id, TimeSpan offset)
		{
			try
			{
				return TimeZoneInfo.FindSystemTimeZoneById(id);
			}
			catch (TimeZoneNotFoundException)
			{
				return TimeZoneInfo.GetSystemTimeZones().First(z => z.BaseUtcOffset == offset);
			}
		}

		/// <summary>
		/// Создать <see cref="Exchange"/>.
		/// </summary>
		public Exchange()
		{
			ExtensionInfo = new Dictionary<object, object>();
		}

		private string _name;

		/// <summary>
		/// Кодовое название биржи.
		/// </summary>
		[DataMember]
		[Identity]
		public string Name
		{
			get { return _name; }
			set
			{
				if (Name == value)
					return;

				_name = value;
				this.Notify("Name");
			}
		}

		private string _rusName;

		/// <summary>
		/// Русскоязычное название биржи.
		/// </summary>
		[DataMember]
		public string RusName
		{
			get { return _rusName; }
			set
			{
				if (RusName == value)
					return;

				_rusName = value;
				this.Notify("RusName");
			}
		}

		private string _engName;

		/// <summary>
		/// Англоязычное название биржи.
		/// </summary>
		[DataMember]
		public string EngName
		{
			get { return _engName; }
			set
			{
				if (EngName == value)
					return;

				_engName = value;
				this.Notify("EngName");
			}
		}

		[field: NonSerialized]
		private TimeZoneInfo _timeZoneInfo = TimeZoneInfo.Utc;

		/// <summary>
		/// Информация о временной зоне, где находится биржа.
		/// </summary>
		[TimeZoneInfo]
		[XmlIgnore]
		public TimeZoneInfo TimeZoneInfo
		{
			get { return _timeZoneInfo; }
			set
			{
				if (value == null)
					throw new ArgumentNullException("value");

				if (TimeZoneInfo == value)
					return;

				_timeZoneInfo = value;
				this.Notify("TimeZoneInfo");
			}
		}

		/// <summary>
		/// Информация о тестовой бирже, не имеющая ограничения в расписание работы.
		/// </summary>
		public static Exchange Test { get; private set; }

		/// <summary>
		/// Информация о бирже ME (Moscow Exchange).
		/// </summary>
		public static Exchange Me { get; private set; }

		/// <summary>
		/// Информация об Украiнська Бiржа.
		/// </summary>
		public static Exchange Ux { get; private set; }

		/// <summary>
		/// Информация о бирже NYSE (New York Stock Exchange).
		/// </summary>
		public static Exchange Nyse { get; private set; }

		/// <summary>
		/// Информация о бирже NASDAQ.
		/// </summary>
		public static Exchange Nasdaq { get; private set; }

		/// <summary>
		/// Информация о бирже LSE (London Stock Exchange).
		/// </summary>
		public static Exchange Lse { get; private set; }

		/// <summary>
		/// Информация о бирже TSE (Tokio Stock Exchange).
		/// </summary>
		public static Exchange Tse { get; private set; }

		/// <summary>
		/// Информация о бирже HKEX (Hong Kong Stock Exchange).
		/// </summary>
		public static Exchange Hkex { get; private set; }

		/// <summary>
		/// Информация о бирже HKFE (Hong Kong Futures Exchange).
		/// </summary>
		public static Exchange Hkfe { get; private set; }

		/// <summary>
		/// Информация о бирже Sse (Shanghai Stock Exchange).
		/// </summary>
		public static Exchange Sse { get; private set; }

		/// <summary>
		/// Информация о бирже SZSE (Shenzhen Stock Exchange).
		/// </summary>
		public static Exchange Szse { get; private set; }

		/// <summary>
		/// Информация о бирже TSX (Toronto Stock Exchange).
		/// </summary>
		public static Exchange Tsx { get; private set; }

		/// <summary>
		/// Информация о бирже FWB (Frankfurt Stock Exchange).
		/// </summary>
		public static Exchange Fwb { get; private set; }

		/// <summary>
		/// Информация о бирже ASX (Australian Securities Exchange).
		/// </summary>
		public static Exchange Asx { get; private set; }

		/// <summary>
		/// Информация о бирже NZSX (New Zealand Stock Market).
		/// </summary>
		public static Exchange Nzsx { get; private set; }

		/// <summary>
		/// Информация о бирже BSE (Bombay Stock Exchange).
		/// </summary>
		public static Exchange Bse { get; private set; }

		/// <summary>
		/// Информация о бирже NSE (National Stock Exchange of India).
		/// </summary>
		public static Exchange Nse { get; private set; }

		/// <summary>
		/// Информация о бирже SWX (SIX Swiss Exchange).
		/// </summary>
		public static Exchange Swx { get; private set; }

		/// <summary>
		/// Информация о бирже KRX (Korea Exchange).
		/// </summary>
		public static Exchange Krx { get; private set; }

		/// <summary>
		/// Информация о бирже MSE (Madrid Stock Exchange).
		/// </summary>
		public static Exchange Mse { get; private set; }

		/// <summary>
		/// Информация о бирже JSE (Johannesburg Stock Exchange).
		/// </summary>
		public static Exchange Jse { get; private set; }

		/// <summary>
		/// Информация о бирже SGX (Singapore Exchange).
		/// </summary>
		public static Exchange Sgx { get; private set; }

		/// <summary>
		/// Информация о бирже TSEC (Taiwan Stock Exchange).
		/// </summary>
		public static Exchange Tsec { get; private set; }

		/// <summary>
		/// Информация о бирже PSE (Philippine Stock Exchange).
		/// </summary>
		public static Exchange Pse { get; private set; }

		/// <summary>
		/// Информация о бирже MYX (Bursa Malaysia).
		/// </summary>
		public static Exchange Myx { get; private set; }

		/// <summary>
		/// Информация о бирже IDX (Indonesia Stock Exchange).
		/// </summary>
		public static Exchange Idx { get; private set; }

		/// <summary>
		/// Информация о бирже SET (Stock Exchange of Thailand).
		/// </summary>
		public static Exchange Set { get; private set; }

		/// <summary>
		/// Информация о бирже CSE (Colombo Stock Exchange).
		/// </summary>
		public static Exchange Cse { get; private set; }

		/// <summary>
		/// Информация о бирже TASE (Tel Aviv Stock Exchange).
		/// </summary>
		public static Exchange Tase { get; private set; }
		
		[field: NonSerialized]
		private IDictionary<object, object> _extensionInfo;

		/// <summary>
		/// Расширенная информация по бирже.
		/// </summary>
		/// <remarks>
		/// Необходима в случае хранения в программе дополнительной информации, ассоциированной с биржей.
		/// </remarks>
		[XmlIgnore]
		[Browsable(false)]
		public IDictionary<object, object> ExtensionInfo
		{
			get { return _extensionInfo; }
			set { _extensionInfo = value; }
		}

		[field: NonSerialized]
		private PropertyChangedEventHandler _propertyChanged;

		event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged
		{
			add { _propertyChanged += value; }
			remove { _propertyChanged -= value; }
		}

		void INotifyPropertyChangedEx.NotifyPropertyChanged(string info)
		{
			_propertyChanged.SafeInvoke(this, info);
		}

		/// <summary>
		/// Получить строковое представление.
		/// </summary>
		/// <returns>Строковое представление.</returns>
		public override string ToString()
		{
			return Name;
		}

		/// <summary>
		/// Сравнить два объекта <see cref="Exchange" /> на эквивалентность.
		/// </summary>
		/// <param name="other">Объект для сравнения.</param>
		/// <returns>true, если другой объект равен текущему, иначе false.</returns>
		protected override bool OnEquals(Exchange other)
		{
			return Name == other.Name;
		}

		/// <summary>
		/// Рассчитать хеш-код объекта <see cref="Exchange"/>.
		/// </summary>
		/// <returns>Хеш-код.</returns>
		public override int GetHashCode()
		{
			return Name.GetHashCode();
		}

		/// <summary>
		/// Создать копию объекта <see cref="Exchange" />.
		/// </summary>
		/// <returns>Копия.</returns>
		public override Exchange Clone()
		{
			return new Exchange
			{
				Name = Name,
				TimeZoneInfo = TimeZoneInfo,
			};
		}
	}
}